/*-------------------------------------------------------------------------
 - Copyright (c) 2024-2025 [XD-AMCC TEAM]
 - [XD-AMCC] is licensed under Mulan PSL v2.
 - You can use this software according to the terms and conditions of the Mulan PSL v2.
 - You may obtain a copy of Mulan PSL v2 at:
 -          http://license.coscl.org.cn/MulanPSL2
 - THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES
 - OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
 - TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 - See the Mulan PSL v2 for more details.
 ------------------------------------------------------------------------*/


/**
 * @brief Internal interface of Lexer and tokens.
 *
 *
 * @author  WXQ#XDU
 * @date    2024.11
 */

#pragma once

#ifndef AMCC__LEX_INNER_H
#define AMCC__LEX_INNER_H

#include "../../include/amcc_list.h"
#include "../../include/amcc_string.h"
#include "../../include/amcc_error.h"
#include "../../include/amcc_log.h"
#include "../../include/token.h"
#include "../../include/lexer.h"

AMCC_DECL_BEGIN


/**
 * Type of the semantic value of a token.
 */
typedef  union token_value {
    token_intvalue_t  intValue; // for integral number, real number
    double         doubleValue; // for real number
} t_token_value;

#ifdef AMCC_USE_CLASS_STRING
#define TXT_TYPE        t_string*
#else
#define TXT_BASETYPE    char*
#define string_new      cstring_new
#define string_destroy  cstring_destroy
#define string_text     cstring_text
#define string_length   cstring_length
#endif

/**
 * Type of token body.
 *
 * Each token body is singleton data shared by multiple tokens,
 * and it is stored in token-pool.
 *
 * The purpose of this scheme is to avoid storing the same data
 * in multiple copies, and thus using less memory resources.
 */
typedef struct token_body {
    EnumTokenKind  kind;        // kind of this token
    TXT_BASETYPE   inputText;   // original text from input file
    t_token_value  value;       // semantic value of this token
} t_token_body;


/**
 * @brief Creates a new token body.
 * @param tokText   pointer to text of a token
 * @param kind      token kind
 */
extern t_token_body * token_body_new(const char * tokText, EnumTokenKind kind);

/**
 * @brief Destroy this token-body.
 * @param pThis   pointer to this token-body
 */
extern void token_body_destroy(t_token_body * pThis);

/**
 * @brief Find a cached token-body according to token-text.
 * @param tokText  pointer to text of a token
 * @return Return a pointer to cached item which text is same as tokText,
 *     otherwise return NULL.
 */
extern t_token_body * tokenpool_find(const char* tokText);
/**
 * @brief Cache a token-body into token pool.
 * @param tokText  pointer to text of a token
 * @param kind     token kind
 * @return Return a pointer to cached item which text is tokText.
 *         Return null on error.
 */
extern t_token_body * tokenpool_put(const char* tokText,
       EnumTokenKind kind, int line, int col, const char * filePath);

extern void tokenpool_init();
extern void tokenpool_destroy();

/**
 * @brief Creates and initializes a new token stream.
 * @param pThis    pointer to this stream
 * @return On success, return a pointer to new token stream.
 * @return On failure, return null.
 */
extern t_tokenstream_ptr tokenstream_new();
/**
 * @brief append new token to tail of this stream.
 * @param pThis   pointer to this stream
 * @param pToken  pointer to a token
 * @return Return 0 on success, or a negative value on failure.
 */
extern int tokenstream_append(t_tokenstream_ptr pThis, t_token_ptr pToken);


extern t_token_ptr* lexer_eof_tokens_default();  // lexer.c

typedef struct  t_word_info {
    t_lexer_ptr  theLexer;
    int   n_scanedchars;  // the number of scanned characters
    EnumTokenKind kind; // out-value, the token kind, TK_ERROR on error
} t_word_info;

extern const char * lexer_style();

/**
 * @brief Simulates a DFA to recognize a token based on the transition table.
 * @param wordInfo (IN, OUT) pointer to word information
 * @return a updated word information.
 *     The returned token-kind is TK_ERROR if the word is can not be accepted.
 */
extern t_word_info dfa_tableDriven_recognize(t_word_info  wordInfo);

/**
 * @brief Recognize a token in manual-coding style.
 * @param wordInfo  (IN, OUT) pointer to word information
 * @return a updated word information.
 *     The returned token-kind is TK_ERROR if the word is can not be accepted.
 */
extern t_word_info manual_coding_recognize(t_word_info  wordInfo);


/**
 * @brief Whether AMCC support the token kind or not.
 * @param tokKind the token kind
 * @return return true only if AMCC does not support it
 */
extern bool token_notSupport(EnumTokenKind tokKind);


extern void lexer_setInputFile(t_lexer_ptr pThis, const char* inputFilePath);
extern int  lexer_open_file(t_lexer_ptr pThis);
extern int  lexer_load_file(t_lexer_ptr pThis);
extern void lexer_close_file(t_lexer_ptr pThis);
extern int  lexer_scan_file(t_lexer_ptr pThis);
extern void lexer_release_resources(t_lexer_ptr pThis, bool bReleaseTokens);


/**
 * @brief Query the correct kind of a ID-like token.
 *
 * @param  text   token text
 * @return Return the correct kind or "TK_ID".
 * @return Return "TK_ERROR" if the text is invalidate.
 */
extern EnumTokenKind token_query_id_kind(const char* text);

/**
 * @brief Creates and initializes a new token.
 *
 * @param pLexer  the lexer that recognize this token
 * @return On success, return a pointer to the new token.
 * @return On failure, return null.
 */
extern t_token_ptr token_new(const char* cString,
                             EnumTokenKind kind,
                             t_lexer_ptr pLexer,
                             int line, int col );

/**
 * @brief Destroy this token and release inner resource.
 * @param pThis   pointer to this token
 */
extern void token_destroy(t_token_ptr pThis);


/**
 * @brief Set the position of the token.
 * @param pThis  pointer to this token
 * @param pos    new position of this token
 */
void token_set_position(t_token_ptr pThis, t_position pos);

/**
 * @brief Fill the semantic-value attribute of this token.
 * @param pThis  pointer to this token
 * @return Return 0 for ok, return -1 for overflow.
 * @note  this function MUST be called after the kind and text are filled.
 */
extern int token_fill_value(t_token_body * pThis);


// -----------------------------------------------------------------------
// API of class char-stream
//

#define READ_CHAR(pLexer)  charstream_readChar(pLexer->charStream)
#define BACK_CHAR(pLexer)  charstream_backChar(pLexer->charStream)

#define CHAR_EOF     '\0'
#define IS_EOF(ch)   (CHAR_EOF == ch)
#define IS_EOL(ch)   ('\r' == ch || '\n' == ch)
#define IS_SPACE(ch) (' ' == ch || '\t' == ch || '\f' == ch || '\v' == ch)

/**
 * @brief Class char-stream whose instance is the input buffer for Lexer.
 *
 *
 */
typedef struct charStream {
    char * pBuffer;         // pointer to the low-level buffer
    long   szBuffer;        // size of the low-level buffer
    long   idxForward;      // forward (lookAhead) position to scan ahead

    // following three fields are designed to avoid double-copies
    // of the lexeme whose length may be unpredictable.
    // Thinking: a long_long_long_long variable name ^_^

    long   idxBeginMarker;  // beginning position of current lexeme
    long   idxEndMarker;    // ending position immediately following
                            // the end of token text from idxBeginMarker
    char   charNextToToken; // the original char at idxEndMarker

    const char * filePath;  // input file path
    FILE       * fpInput;   // handler of input file
} t_charstream,  * t_charstream_ptr;

/**
 * @brief Creates a new char-stream as input buffer.
 *
 * This function will load the entire file content into buffer.
 *
 * @param fp        handler of input file
 * @param filePath  path of input file
 * @return A pointer to the new char-stream.
 */
extern t_charstream_ptr charstream_new(FILE * fp, const char* filePath);

/**
 * @brief Destroys the specified char-stream.
 * @param pThis  pointer to this char-stream object
 */
extern void charstream_destroy(t_charstream_ptr pThis);

/**
 * @brief Read a character from a char-stream.
 *
 * The internal file position indicator is advanced to the next character.
 *
 * @param pThis  pointer to this char-stream object
 * @return Returns the character read, or
 *    returns CHAR_EOF to indicate a end-of-file condition.
 *
 * @see charstream_backChar()
 */
extern char charstream_readChar(t_charstream_ptr pThis);

/**
 * @brief Put a character back onto the char-stream.
 *
 * The internal file position indicator is retreated as if a previous
 * charstream_readChar() operation was undone.
 *
 * @param pThis  pointer to this char-stream object
 *
 * @return  Return true if at least one character that had been read
 *     has not been put back, otherwise return false.
 */
extern bool charstream_backChar(t_charstream_ptr pThis);

/**
 * @brief Marks the char last read as the beginning of a new token.
 *
 * @param pThis  pointer to this char-stream object
 */
extern void charstream_markBegin(t_charstream_ptr pThis);

/**
 * @brief Marks the char last read is the next position immediately
 *        following the end of a token text which beginning position
 *        has marked through markBegin().
 *
 * @param pThis  pointer to this char-stream object
 */
extern void charstream_markEnd(t_charstream_ptr pThis);

/**
 * @brief Get the string which is marked by markBegin() and markEnd().
 *
 * @param pThis  pointer to this char-stream object
 * @return A pointer to the C-string which is terminated
 *         with a null character.
 */
extern const char * charstream_markedText(t_charstream_ptr pThis);

/**
 * @brief Get the length of the string which is marked by
 *        markBegin() and markEnd().
 * @param pThis  pointer to this char-stream object
 * @return If caller correctly invoked markBegin() and markEnd(),
 *         return a positive integer for the length.
 *         Otherwise, return a non-positive integer.
 */
extern int charstream_markedText_length(t_charstream_ptr pThis);


AMCC_DECL_END

#endif //AMCC__LEX_INNER_H
